const { ExternalModule } = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin');
/**
 * 1.如果你加载的 jquery要走类似于external模块的逻辑 
 * 2. 生产html文件的时候，要注意自动添加CDN的链接，知道 我依赖没有依赖这个模块
 */
class AutoExternalPlugin {
    constructor(options) {
        this.options = options;
        this.importedModules = new Set();//放着所有的导入的并依赖的模块
        this.externalModules = Object.keys(this.options);//[jquery,lodash]
    }
    apply(compiler) {
        //获取 普通模块工厂
        compiler.hooks.normalModuleFactory.tap('AutoExternalPlugin',(normalModuleFactory)=>{
            //获取JS模块的parser 
            normalModuleFactory.hooks.parser
            .for('javascript/auto')//可以兼容import es 和 commonjs require
            .tap('AutoExternalPlugin',parser=>{
                //如果真的导入了一个任何一个模块的话，都会添加到importedModules 里
                parser.hooks.import.tap('AutoExternalPlugin',(statement,source)=>{
                    /*
                    console.log(statement);
                    console.log(source);
                    */
                    if(this.externalModules.includes(source)){
                        this.importedModules.add(source);
                    }
                });
                //如果通过require方法引入了个模块的话
                parser.hooks.call.for('require').tap('AutoExternalPlugin',(expression)=>{
                    let value = expression.arguments[0].value;
                    if(this.externalModules.includes(value)){
                        this.importedModules.add(value);//lodash
                    }
                })
            })
            //这是真正用来生产模块的钩子
            normalModuleFactory.hooks.factorize.tapAsync('AutoExternalPlugin',(resolveData,callback)=>{
                let request = resolveData.request;//你将要加载的资源 ./src/index.js jquery lodash
                //let externalModules = Object.keys(this.options);//['jquery','lodash']
                if(this.externalModules.includes(request)){
                    //constructor(request?: any, type?: any, userRequest?: any);
                    let expose = this.options[request].expose;//jquery=>jQuery lodash=>_
                    //如果第二个参数不为空，则表示返回了函数 factorize SerialBailHook 相当这个工厂就直接生产出了这个模块，，跳过后续的所有钩子
                    //callback(null,new ExternalModule(expose,'window',request));
                    callback(null);
                }else{//不传参，走真正的原始的生产模块的逻辑 ，就会生产出来个NormalModule
                    callback(null);
                }
            });
        });
        compiler.hooks.compilation.tap('AutoExternalPlugin',(compilation)=>{
            HtmlWebpackPlugin.getHooks(compilation).alterAssetTags.tapAsync('AutoExternalPlugin',(htmlData,callback)=>{
                let {assetTags} =htmlData;
                let importedExternalModules = Object.keys(this.options).filter(item=>this.importedModules.has(item));
                importedExternalModules.forEach(key=>{
                    assetTags.scripts.unshift({
                        tagName: 'script',//标签类型
                        voidTag: false,//是否为空
                        attributes: {
                            src:this.options[key].url,defer:false
                        }
                      });
                });
                //传给下一个钩子或者回调
                callback(null,htmlData);
            });
        });
    }
}
module.exports = AutoExternalPlugin;
/**
 * Node {
  type: 'ImportDeclaration',
  start: 0,
  end: 23,
  loc: SourceLocation {
    start: Position { line: 1, column: 0 },
    end: Position { line: 1, column: 23 }
  },
  range: [ 0, 23 ],
  specifiers: [
    Node {
      type: 'ImportDefaultSpecifier',
      start: 7,
      end: 8,
      loc: [SourceLocation],
      range: [Array],
      local: [Node]
    }
  ],
  source: Node {
    type: 'Literal',
    start: 14,
    end: 22,
    loc: SourceLocation { start: [Position], end: [Position] },
    range: [ 14, 22 ],
    value: 'jquery',
    raw: "'jquery'"
  }
}
jquery
 */